<?php

// @formatter:off
/**
 * @file WCImage.php
 * @author Alejandro Dario Simi
 * @date $Date: 2014-06-03 03:40:32 +0000 (Tue, 03 Jun 2014) $
 *
 * $Id: WCImage.php 112 2014-06-03 03:40:32Z daemonraco@gmail.com $
 * $URL: http://wcomix.googlecode.com/svn/trunk/includes/representations/WCImage.php $
 */
// @formatter:on

/**
 * @class WCImage This class represents entries saved in the table "%images".
 */
class WCImage extends WCRepresentationGroup implements WCPreviews, WCSearchIndexable, WCHardRemove, WCTrashed, WCFlowItem {
	/**
	 * @var string[] This variable holds the list of columns that cannot be
	 * directly modified by the pseudo-method __set().
	 * In this case img_id, img_group, img_directory and img_name.
	 */
	protected $_PropertiesReadOnly = array(
		"id",
		"group",
		"directory",
		"name"
	);
	/**
	 * @var string[] This variable holds the list of columns that must be
	 * treated as booleans and then send to the database as 'Y' and 'N' by
	 * the pseudo-method __set().
	 * In this case img_checked, img_generated, img_indexed, img_rated and
	 * img_lost.
	 */
	protected $_PropertiesBoolean = array(
		"checked",
		"generated",
		"indexed",
		"rated",
		"lost"
	);
	/**
	 * @var string This is the prefix of every column on the table
	 * '%images'.
	 */
	protected $_PropertiesPrefix = "img_";
	/**
	 * @var string This is the name of the represented table. In this case,
	 * '%images'.
	 */
	protected $_PropertiesTable = "images";
	/**
	 * @var WCCollection This is a pointer to the collection that contains
	 * this image.
	 */
	protected $_collection = null;
	/**
	 * @var WCCollection This is a pointer to the directory where this image
	 * is located.
	 */
	protected $_directory = null;
	protected $_hardTrashed = false;
	/**
	 * @var string This is the root path of the group that contains this
	 * image.
	 */
	protected $_root = false;
	/**
	 * @var boolean This flag indicates that full paths and URIs for previews
	 * were loaded or not.
	 */
	protected $_pathsLoaded = false;
	/**
	 * @var string This is the image full path of the image's thumbnail. For
	 * example:
	 * 	- /var/www/wcomix/cache/thumbs/img_1/7/007/7007efdc84400474bc19ed442dbfeccc.png
	 */
	protected $_previewPath = false;
	/**
	 * @var string This is similar to '$_previewPath', but it is the URI.
	 * It is a full path relative to the server. For example:
	 * 	- /wcomix/cache/thumbs/img_1/7/007/7007efdc84400474bc19ed442dbfeccc.png
	 */
	protected $_previewUri = false;
	/**
	 * @var string This is the full-path where the image is temporally
	 * expanded in order to be shown in the web page. For example:
	 * 	-
	 * /var/www/wcomix/cache/expanded/img_1/0/c96/0c9610e6361e88791b85c132ddecdac4.png
	 */
	protected $_publicPath = false;
	/**
	 * @var string This is similar to $_publicPath but is only the URI to the
	 * expanded directory.
	 * It is a full path relative to the server. For example:
	 * 	- /wcomix/cache/expanded/img_1/0/c96/0c9610e6361e88791b85c132ddecdac4.png
	 */
	protected $_publicUri = false;
	protected $_path = false;
	protected $_trashed = false;
	protected $_trashedFlagsLoaded = false;
	//
	// Magic methods.
	/**
	 * Class basic constructor
	 */
	public function __construct() {
		//
		// Calling the parent class constructor.
		parent::__construct();
	}
	public function __toString() {
		return $this->ok() ? "{$this->name()} ({$this->id()}[g:{$this->group},d:{$this->directory}])" : "broken item";
	}
	//
	// Public methods.
	/**
	 * This method allows to load and retrieve the collection where this image
	 * is held.
	 *
	 * @return WCCollection Returns a collection pointer. When the image has
	 * no collection or if there where a problem retrieving it, it returns
	 * a null pointer.
	 */
	public function collection() {
		//
		// Checking if the image belongs to a collection.
		if($this->collection && !$this->_collection) {
			//
			// Loading the collections holder.
			global $wcCollectionsHolder;
			//
			// Asking for the collection to the holder.
			$this->_collection = $wcCollectionsHolder->item($this->collection);
			//
			// If the collection is found and it has no issues, it can
			// be returned, otherwise, returns null.
			if(!$this->_collection || !$this->_collection->ok()) {
				$this->_collection = null;
			}
		}
		//
		// Returning the found collection.
		return $this->_collection;
	}
	/**
	 * This method allows to get a pointer to the directory where the image is
	 * located.
	 *
	 * @return WCDirectory Returns a directory pointer.
	 */
	public function directory() {
		//
		// Checking id the pointer was not already loaded.
		if(!$this->_directory) {
			//
			// Asking for the directory to the holder.
			$this->_directory = WCDirectoriesHolder::I()->item($this->directory);
			//
			// If the directory is found and it has no issues it can
			// be returned, otherwise, the pointer remains null.
			if(!$this->_directory || !$this->_directory->ok()) {
				$this->_directory = null;
			}
		}
		// Returning the found pointer.
		return $this->_directory;
	}
	/**
	 * This method expand all of this image contents into the expansion
	 * directory.
	 * @todo check
	 *
	 * @return boolean Returns true when the image was successfully expanded.
	 */
	public function expand() {
		//
		// Checking that this image has no issues, that it is not lost and
		// that it's not already expanded.
		if($this->ok() && !$this->isPublished() && !$this->lost) {
			//
			// Loading global default settings.
			// @{
			global $wcDefaults;
			global $wcPaths;
			// @}
			//
			// Checking if the expansion directory must be physically
			// created.
			if(!is_dir(dirname($this->publicPath()))) {
				//
				// Physically creating the directory.
				mkdir(dirname($this->publicPath()), $wcDefaults["directory-permissions"], true);
			}
			//
			// Checking that it is possible to read the file.
			if(is_readable($this->path())) {
				//
				// Copying image to a public path and checking the
				// operation.
				if(!copy($this->path(), $this->publicPath())) {
					trigger_error(__CLASS__.": Unable to publish image '{$this->name}'", E_USER_ERROR);
				}
			} elseif(!is_file($this->path())) {
				$this->lost = true;
				$this->reload();
			}
		}
		//
		// The result of this method is the same one of 'isPublished()'.
		return $this->isPublished();
	}
	public function flowItemType() {
		return WCFlow::ItemTypeImage;
	}
	/**
	 * This method creates a the previews list based on data from the object,
	 * and caches this data for further uses.
	 *
	 * @return boolean Returns true if a previews list was generated.
	 */
	public function generatePreviews() {
		//
		// Setting a default value to be returned.
		$out = false;
		//
		// Checking that there no issues with this image and that it's
		// preview is not yet generated.
		if($this->ok() && !$this->hasValidPreviews()) {
			$imgData = @getimagesize($this->path());
			if($imgData !== false) {
				$imgData = array_merge($imgData, pathinfo($this->path()));
				$imgData["width"] = $imgData[0];
				$imgData["height"] = $imgData[1];

				$previewPath = WCSanitizer::WComixPath("/img_{$this->group}{$this->expanded}.{$imgData["extension"]}");
				$out = WCPreviewsManager::I()->createPreview($this->group(), WCPreviewsManager::TypeImage, $this->id, 0, $previewPath, $this->path());
				//
				// Checking that it is possible to read the file.
				if($out && is_readable($this->path())) {
					//
					// Updating image's MD5 hash.
					$this->md5 = md5(file_get_contents($this->path()));
					//
					// Updating image's SHA-1 hash.
					$this->sha1 = sha1_file($this->path());
					//
					// Updating sizes.
					$this->width = $imgData["width"];
					$this->height = $imgData["height"];
					$this->size = filesize($this->path());

					$this->setValidPreviews(true);

					$out = true;
				} else {
					$out = false;
				}
				//
				// Setting to false the flag that controls preview paths.
				$this->_pathsLoaded = false;
				//
				// Loading preview paths.
				$this->loadPaths();
			} else {
				$out = false;
			}
		} elseif($this->hasPreview()) {
			$out = true;
		}
		//
		// Returning result.
		return $out;
	}
	public function hardRemove() {
		$out = false;

		$filename = WCSanitizer::SystemPath("{$this->directory()->fullPath()}/{$this->name}");
		if(@unlink($filename)) {
			$out = $this->remove();
		}

		return $out;
	}
	/**
	 * This method allows to know if the image has a preview.
	 *
	 * @return boolean Returns true when the image preview exists.
	 */
	public function hasPreview() {
		//
		// Loading paths.
		$this->loadPaths();
		//
		// Returns true when the preview image exists.
		return $this->_previewPath != false;
	}
	/**
	 * This method allows to know if the object has a list of preview images.
	 *
	 * @return boolean It always returns false.
	 */
	public function hasPreviews() {
		return false;
	}
	public function hasValidPreviews() {
		return $this->generated;
	}
	/**
	 * This method allows to know whether an item is indexed or not. Also it
	 * allows to change such status.
	 *
	 * @param boolean $value New status to be set. When absent, this method
	 * works as a getter.
	 * @return boolean Returns true when the object is indexed.
	 */
	public function indexed($value = null) {
		if(!is_null($value)) {
			$this->indexed = $value;
		}

		return $this->indexed;
	}
	public function isHardTrashed() {
		$this->loadTrashedFlags();
		return $this->_hardTrashed;
	}
	/**
	 * This method allows to if the image is already expanded.
	 *
	 * @return boolean Returns true when the expansion directory exists.
	 */
	public function isPublished() {
		return is_file($this->publicPath());
	}
	public function isTrashed() {
		$this->loadTrashedFlags();
		return $this->_trashed;
	}
	/**
	 * This method load the a image based on its information saved in the
	 * table '%images'.
	 *
	 * @param int $id Identificator for the image.
	 * @param WCGroup $group Group that contains the image to load.
	 * @return boolean Retruns true when there were no problems loading the
	 * image.
	 */
	public function load($id) {
		//
		// Because image object loading may require may require more than
		// one try to get all the information, this flag indicates if
		// loadings should keep going on.
		$keepLoading = true;
		while($keepLoading) {
			//
			// By default, it is considered that only one try is
			// enough.
			$keepLoading = false;
			//
			// Calling the 'load()' method of parent class
			// WCGTableRepresentation
			parent::load($id);
			//
			// Checking if the database entry loaded without any
			// problem and it represents valid image.
			if($this->ok()) {
				//
				// If there are neither a thumbnail's URI nor a
				// expanded URI, these must be generated.
				if($this->id !== false && !$this->expanded) {
					//
					// Creating a hash code based on
					// properties of the current image.
					$code = md5("{$this->group}|{$this->directory()->name}|{$this->name}");
					//
					// Setting a URI for expansion directory.
					$this->expanded = "/{$code[0]}/{$code[1]}{$code[2]}{$code[3]}/{$code}";
					//
					// At this point, many properties were
					// modified on the database and to avoid
					// inconsistencies, loading must be
					// retried.
					$keepLoading = true;
				}
				//
				// Setting the image root directory with group's
				// root path.
				$this->_root = $this->group()->root();
				//
				// Checking some issues:
				//	- A not null id means the parent class
				//	  loaded the image without problems.
				//	- A value in the directory property means
				//	  the image is not located in the limbus
				//	  O_o
				//	  Zero isn't a valid id either.
				//	- If the image is flagged as lost, it is
				//	  no good for normal usage.
				$this->_ok = ($this->id !== false && $this->directory && !$this->lost);
			}
		}
		//
		// Return the same result than method 'ok()'.
		return $this->ok();
	}
	/**
	 * This method allows to get the name of the image. It is different to
	 * directly use the property 'name' through the pseudo-method __get()
	 * because, in this case, if the image is scheduled to be rename, it
	 * returns the name it will have.
	 * This provides a transparent use of the image name beyond any renaming.
	 *
	 * @return string Returns the name of the represented image.
	 */
	public function name() {
		//
		// Choosing and returning the right name.
		return $this->next_name ? $this->next_name : $this->name;
	}
	public function path() {
		//
		// Loading paths.
		$this->loadPaths();
		//
		// Returning path.
		return $this->_path;
	}
	/**
	 * This method is similar to 'previewPath()', but for the URI.
	 *
	 * @return string Returns a URI.
	 */
	public function preview() {
		//
		// Loading global pointers.
		global $wcDefaults;
		//
		// Loading paths.
		$this->loadPaths();
		//
		// Returning the URI.
		return $this->hasPreview() ? $this->_previewUri : $wcDefaults["preview"];
	}
	/**
	 * This method allows to get a full path of this image's thumbnail.
	 *
	 * @return string Returns a full path.
	 */
	public function previewPath() {
		//
		// Loading paths.
		$this->loadPaths();
		//
		// Returning the full path.
		return $this->_previewPath;
	}
	/**
	 * This method allows to get the object's list of preview images.
	 *
	 * @return string[] Always returns an empty array.
	 */
	public function previews() {
		return array();
	}
	/**
	 * This method allows to get the system full path where all elements
	 * extracted from the image will be temporarily stored.
	 *
	 * @return string Returns a system full path.
	 */
	public function publicPath() {
		//
		// Loading paths.
		$this->loadPaths();
		//
		// Returning path.
		return $this->_publicPath;
	}
	/**
	 * This method allows to get the URI where all elements extracted from the
	 * image will be temporarily stored.
	 *
	 * @return string Returns a URI relative to the server.
	 */
	public function publicUri() {
		//
		// Loading paths.
		$this->loadPaths();
		//
		// Returning the URI.
		return $this->_publicUri;
	}
	public function reload() {
		$this->_collection = null;
		$this->_directory = null;
		$this->_root = false;
		$this->_pathsLoaded = false;
		$this->_previewPath = false;
		$this->_previewUri = false;
		$this->_publicPath = false;
		$this->_publicUri = false;
		$this->_path = false;

		return parent::reload();
	}
	/**
	 * This method allows to get the group root path where the image is
	 * physically stored.
	 *
	 * @return string Returns a full path to the group's directory.
	 */
	public function root() {
		//
		// Returning path.
		return $this->_root;
	}
	/**
	 * This method allows to physically move a image from a directory to
	 * another.
	 *
	 * Even though this operation can be done manually by any system user,
	 * this method will also keep all associations made with the image.
	 * Otherwise, a manual operation will cause two things:
	 * 	- the new path of the image will be taken as a new image.
	 * 	- and the current image will become lost.
	 * And all this thing will occur on the next cron execution. Meanwhile
	 * many undesired behaviors will be seen.
	 *
	 * @param WCDirectory $directory Directory where the image will be moved
	 * to.
	 * @return boolean Returns true where the operation was performed without
	 * any issue.
	 */
	public function setDirectory(WCDirectory &$directory) {
		//
		// Loading global pointers.
		global $wcDefaults;
		//
		// This is the default value to be returned.
		$out = false;
		//
		// Checking that:
		//	- Read Only Mode is not activated.
		//	- there are no issues with the current image
		//	- there are no issues with the destination directory.
		//	- the image and the destination directory belong to the
		//	  same group.
		//	- The image is not already in the destination directory.
		if(!$wcDefaults["read-only"] && $this->ok() && $directory->ok() && $this->group == $directory->group && $directory->id() != $this->directory) {
			//
			// Setting that it's ok to continue with the operation.
			$ok = true;
			//
			// Creating an auxiliary pointer to the current image
			// directory.
			$curDirectory = $this->directory();
			//
			// Building all needed paths.
			// @{
			$dirPathFrom = WCSanitizer::SystemPath("{$this->group()->root}{$curDirectory->name}");
			$dirPathTo = WCSanitizer::SystemPath("{$this->group()->root}{$directory->name}");
			$imagePathFrom = WCSanitizer::SystemPath("{$dirPathFrom}/{$this->name}");
			$imagePathTo = WCSanitizer::SystemPath("{$dirPathTo}/{$this->name}");
			// @}
			//
			// Checking if it's possible to write in the current
			// image's directory.
			if($ok && !is_writable($dirPathFrom)) {
				//
				// If it's not possible, an exception is raised.
				trigger_error(__CLASS__."::".__FUNCTION__."(): Writting access denied on directory '{$dirPathFrom}'", E_USER_ERROR);
				$ok = false;
			}
			//
			// Checking if it's possible to write into destination
			// directory.
			if($ok && !is_writable($dirPathTo)) {
				//
				// If it's not possible, an exception is raised.
				trigger_error(__CLASS__."::".__FUNCTION__."(): Writting access denied on directory '{$dirPathTo}'", E_USER_ERROR);
				$ok = false;
			}
			//
			// Checking if if the current image file exits.
			if($ok && !file_exists($imagePathFrom)) {
//				//
//				// If not, an exception is raised.
//				trigger_error(__CLASS__."::".__FUNCTION__."(): Original image '{$imagePathFrom}' does not exists", E_USER_ERROR);
				$this->lost = true;
				$ok = false;
			}
			//
			// Checking if if destination file doesn't exits.
			if($ok && file_exists($imagePathTo)) {
				//
				// If not, an exception is raised.
				trigger_error(__CLASS__."::".__FUNCTION__."(): Destination image '{$imagePathTo}' already exists", E_USER_ERROR);
				$ok = false;
			}
			//
			// If everything is ok, the image is moved.
			if($ok) {
				//
				// Moving the image.
				rename($imagePathFrom, $imagePathTo);
				//
				// Checking that the new path exists and the old
				// one doesn't.
				if(is_file($imagePathFrom) || !is_file($imagePathTo)) {
					//
					// If any of this didn't happened, an
					// exception is raised.
					trigger_error(__CLASS__."::".__FUNCTION__."(): Unable to move image '{$this->name}'", E_USER_ERROR);
					$ok = false;
				}
			}
			//
			// If everything is ok, it means that the image was
			// successfully moved
			if($ok) {
				//
				// Cleaning public path.
				@unlink($this->publicPath());
				//
				// Cleaning thumbnail path.
				@unlink($this->previewPath());
				//
				// Since image was moved destination directory's
				// previews are no longer valid.
				$directory->setValidPreviews(false);
				//
				// Since image was moved destination directory's
				// counts should be increased in one.
				$directory->icount++;
				//
				// New directory object should reload to avoid
				// further problems.
				$directory->reload();
				//
				// Since image was moved origin directory's
				// previews are no longer valid.
				$curDirectory->setValidPreviews(false);
				//
				// Since image was moved origin directory's
				// counts should be decreased in one.
				$curDirectory->icount--;
				//
				// Old directory object should reload to avoid
				// further problems.
				$curDirectory->reload();
				//
				// If there's one, collection's previews are also
				// no longer valid.
				if($this->collection()) {
					$this->collection()->setValidPreviews(false);
				}
				//
				// Preparing a statement to update the image.
				// @{
				$stmt = false;
				$stmtID = __CLASS__."::".__FUNCTION__;
				if($this->_db->has($stmtID)) {
					$stmt = $this->_db->get($stmtID);
				} else {
					$query = "update  {$this->_dbprefix}images\n";
					// Current directory should be the new one.
					$query .= "set     img_directory = :directory_s,\n";
					// Expansion URI changed.
					$query .= "        img_expanded  = '',\n";
					// Since some URIs changed, it should be
					// regenerated.
					$query .= "        img_generated = 'N'\n";
					$query .= "where   img_group     = :group\n";
					$query .= " and    img_id        = :image\n";

					$stmt = $this->_db->prepare($stmtID, $query);
				}
				// @}
				//
				// Sending updates to the database.
				$stmt->execute(array(
					":directory_s" => $directory->id,
					":group" => $this->group,
					":image" => $this->id
				));

				$this->indexed = false;
				//
				// Many properties of the image had changed,
				// therefore, to avoid problems, it is reloaded.
				$out = $this->reload();
			}
		}
		//
		// Returning the operation results.
		return $out;
	}
	/**
	 * This method performs the scheduled renaming operation.
	 *
	 * @return boolean Returns true when the operation was performed without
	 * issues. If the image was not scheduled for renaming, it returns false.
	 */
	public function setNextName() {
		//
		// Loading global pointers.
		global $wcDefaults;
		//
		// Setting default value to be returned.
		$ok = false;
		//
		// Checking that Read Only Mode is not activated.
		if(!$wcDefaults["read-only"]) {
			//
			// This is the default value to be returned.
			$ok = true;
			//
			// Generating full paths for origin and destination.
			$oldPath = WCSanitizer::SystemPath("{$this->directory()->fullPath()}/{$this->name}");
			$newPath = WCSanitizer::SystemPath("{$this->directory()->fullPath()}/{$this->next_name}");
			//
			// Making some preliminary checks:
			//	- If the image wasn't scheduled, it is an error.
			//	- If the current full path isn't a file, it is an
			//	  error.
			//	- If the destination full path is an existing
			//	  file, it is an error.
			$ok = $this->next_name != "" && is_file($oldPath) && !file_exists($newPath);
			//
			// If everything is ok, the file is renamed.
			if($ok) {
				//
				// Renaming the file.
				rename($oldPath, $newPath);
				//
				// Checking that the new file exists and the old
				// one doesn't.
				$ok = !is_file($oldPath) && is_file($newPath);
			}
			//
			// If the image was successfully renamed
			if($ok) {
				//
				// Cleaning public path.
				@unlink($this->publicPath());
				//
				// Cleaning thumbnail path.
				@unlink($this->previewPath());
				//
				// Since image preview will be regenerated, its
				// directory's previews are no longer valid.
				$this->directory()->setValidPreviews(false);
				//
				// If there's one, collection's previews are also
				// no longer valid.
				if($this->collection()) {
					$this->collection()->setValidPreviews(false);
				}
				//
				// Preparing a statement to update the image.
				// @{
				$stmt = false;
				$stmtID = __CLASS__."::".__FUNCTION__."[images]";
				if($this->_db->has($stmtID)) {
					$stmt = $this->_db->get($stmtID);
				} else {
					$query = "update  {$this->_dbprefix}images\n";
					// Current name should be the new one.
					$query .= "set     img_name      = :name_s,\n";
					// Image is no longer scheduled for
					// renaming.
					$query .= "        img_next_name = '',\n";
					// Expansion URI changed.
					$query .= "        img_expanded  = '',\n";
					// Since some URIs changed, it should be
					// regenerated.
					$query .= "        img_generated = 'N',\n";
					// It's name changed, so, it must be
					// re-indexed by the search engine.
					$query .= "        img_indexed   = 'N'\n";
					$query .= "where   img_group = :group\n";
					$query .= " and    img_id    = :id\n";

					$stmt = $this->_db->prepare($stmtID, $query);
				}
				// @}
				//
				// Sending updates to the database.
				$stmt->execute(array(
					":name_s" => $this->next_name,
					":group" => $this->group,
					":id" => $this->id
				));

				$this->indexed = false;
				//
				// Many properties of the image had changed,
				// therefore, to avoid problems, it is reloaded.
				$ok = $this->reload();
			}
			if(!$ok && $oldPath == $newPath) {
				$this->next_name = "";
			}
		}
		//
		// Returning the process results.
		return $ok;
	}
	public function setValidPreviews($valid = true) {
		$this->generated = $valid;
		WCViewManager::I()->invalidate(WCViewManager::KeyImage, $this->id());
	}
	public function shrink() {
		//
		// Checking that this image has no issues, that it is not lost and
		// that it's already expanded.
		if($this->ok() && $this->isPublished() && !$this->lost) {
			if(is_readable($this->publicPath())) {
				unlink($this->publicPath());
			}
		}
		//
		// The result of this method is the same one of 'isPublished()'.
		return $this->isPublished();
	}
	/**
	 * This method allows to get a list of phrases that must be associated
	 * with an item.
	 *
	 * @return string[] Returns a list of phrases.
	 */
	public function words() {
		$words = WCSearch::StringToPatterns($this->name());
		foreach($this->directory()->words() as $word) {
			$words[] = $word;
		}
		if($this->collection > 0) {
			foreach($this->collection()->words() as $word) {
				$words[] = $word;
			}
		}
		foreach(WCTagsHolder::I()->tagsOfImage($this) as $tag) {
			foreach($tag->words() as $word) {
				$words[] = $word;
			}
		}
		return $words;
	}
	//
	// Protected methods.
	protected function fixExtension() {
		$pathInfo = pathinfo($this->path());
		$imageInfo = @getimagesize($this->path());
		if($imageInfo) {
			$renamed = false;

			switch($imageInfo["mime"]) {
				case "image/jpeg":
					$this->next_name = "{$pathInfo["filename"]}.jpg";
					$this->thumbs_ext = "jpg";
					$renamed = true;
					break;
				case "image/png":
					$this->next_name = "{$pathInfo["filename"]}.png";
					$this->thumbs_ext = "png";
					$renamed = true;
					break;
				case "image/gif":
					$this->next_name = "{$pathInfo["filename"]}.gif";
					$this->thumbs_ext = "gif";
					$renamed = true;
					break;
			}

			if($renamed) {
				$this->setNextName();
			}
		}
	}
	/**
	 * This method auto-sets the paths of the physical previews and their
	 * URIs.
	 */
	protected function loadPaths() {
		//
		// Checking that there are no issues with this object and that
		// this operation was not run before.
		if($this->ok() && !$this->_pathsLoaded) {
			//
			// Loading global path settings.
			global $wcPaths;

			$previews = WCPreviewsManager::I()->getPreviews($this->group(), WCPreviewsManager::TypeImage, $this->id);

			if(count($previews) > 0 && isset($previews[0])) {
				$this->_previewPath = WCSanitizer::SystemPath("{$wcPaths["cache-directory"]}/{$wcPaths["cache-thumbs-dirname"]}/{$previews[0]}");
				$this->_previewUri = WCSanitizer::WComixPath("{$wcPaths["cache-directory-uri"]}/{$wcPaths["cache-thumbs-dirname"]}/{$previews[0]}");
			} else {
				$this->_previewPath = false;
				$this->_previewUri = false;
				$this->setValidPreviews(false);
			}
			//
			// Setting full path and URI.
			$this->_publicPath = WCSanitizer::SystemPath("{$wcPaths["cache-directory"]}/{$wcPaths["cache-expand-dirname"]}/img_{$this->group}{$this->expanded}.{$this->thumbs_ext}");
			$this->_publicUri = WCSanitizer::SystemPath("{$wcPaths["cache-directory-uri"]}/{$wcPaths["cache-expand-dirname"]}/img_{$this->group}{$this->expanded}.{$this->thumbs_ext}");
			//
			// Setting full path and URI.
			$this->_path = WCSanitizer::SystemPath("{$this->directory()->fullPath()}/{$this->name}");
			//
			// Setting the flag to "already done".
			$this->_pathsLoaded = true;
		}
	}
	protected function loadTrashedFlags() {
		if($this->ok() && !$this->_trashedFlagsLoaded) {
			$trashDir = $this->group()->trash();
			$this->_hardTrashed = $trashDir && $this->directory == $trashDir->id;

			$trashTag = WCActiveTagsHolder::I()->get(WC_ACTIVE_TAG_TRASH, $this->group())->tag();
			$this->_trashed = $trashTag && $trashTag->ok() && $trashTag->hasImage($this);

			$this->_trashedFlagsLoaded = true;
		}
	}
	/**
	 * This is an internal method called by the pseudo-method __set()
	 *
	 * @param string $name Name of the parameter being modified.
	 * @param string $value Value to set to the property with name $name.
	 * @param string $realValue Value built by the pseudo-method __set().
	 * @return boolean Returns true when it is alright to modify the property.
	 */
	protected function subSet($name, &$value, &$realValue) {
		//
		// Checking property rules.
		switch($name) {
			case "next_name":
				//
				// When the property being modified is the class
				// name, any search index on it is no longer
				// valid and it should be updated ASAP.
				$this->indexed = false;
				break;
			case "collection":
				//
				// When the property being modified is the
				// collection, last collector user has to be
				// updated.
				{
					global $wcUser;
					$this->collector = $wcUser->id;
				}
				$this->indexed = false;
				break;
		}
		//
		// There are no conditions in this method that my interrupt the
		// property change.
		return true;
	}
	//
	// Public class methods.
	/**
	 * This is a sorting method based on image id and it can be used on usort
	 * and uasort functions.
	 *
	 * @param WCImage $a Left operand.
	 * @param WCImage $b Right operand.
	 * @return boolean Returns true when the id of $a is greater than $b's.
	 */
	public static function IdSort(WCImage &$a, WCImage &$b) {
		return $a->id > $b->id;
	}
	/**
	 * This is a sorting method based on image names and it can be used on
	 * usort and uasort functions.
	 *
	 * @param WCImage $a Left operand.
	 * @param WCImage $b Right operand.
	 * @return boolean Returns true when the name of $a is greater than
	 * $b's. Since it uses method 'name()' it considers img_name and
	 * img_next_name as well.
	 */
	public static function NameSort(WCImage &$a, WCImage &$b) {
		return $a->name() > $b->name();
	}
	/**
	 * This is the general sorting method and it can be used on usort and
	 * uasort functions.
	 *
	 * @param WCImage $a Left operand.
	 * @param WCImage $b Right operand.
	 * @return boolean Returns true when $a goes after $b.
	 */
	public static function Sort(WCImage &$a, WCImage &$b) {
		return self::NameSort($a, $b);
	}
}
